/**
 * 
 */
package com.taobao.api;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;

import com.taobao.api.model.TaobaoResponse;
import com.taobao.api.util.FetchUtil;

/**
 * 使用<a href="http://hc.apache.org/httpcomponents-client/">commons-httpclient-3.x</a>
 * 实现的UrlFetch接口，推荐在生产环境下使用。一般情况下默认配置即可。
 * 
 * 因为httpclient4重写了httpclient3,所以使用httpclient4的朋友请使用
 * HttpClient4UrlFetch
 * 
 * @author sulinchong.pt
 * @author <a href="mailto:zixue@taobao.com">zixue</a>
 */
public class HttpClientUrlFetch extends AbstractUrlFetch {
	public static final String SIP_STATUS_OK = "9999";

	private boolean keepAlive = false;

	// 执行postMethod
	private HttpClient httpClient;

	public HttpClientUrlFetch() {
		initHttpClient();
	}

	private void initHttpClient() {
		MultiThreadedHttpConnectionManager connectionManger = new MultiThreadedHttpConnectionManager();
		HttpConnectionManagerParams params = new HttpConnectionManagerParams();
		params.setMaxTotalConnections(DEFAULT_MAX_TOTAL_CONNECTIONS);
		params.setDefaultMaxConnectionsPerHost(DEFAULT_MAX_CONNECTIONS_PER_HOST);
		params.setConnectionTimeout(DEFAULT_CONNECT_TIMEOUT);
		params.setSoTimeout(DEFAULT_READ_TIMEOUT);
		params.setStaleCheckingEnabled(DEFAULT_STALE_CHECKING_ENABLED);
		connectionManger.setParams(params);
		httpClient = new HttpClient(connectionManger);
	}

	public TaobaoResponse fetch(URL url, Map<String, CharSequence> payload)
			throws TaobaoApiException {
		return this.fetchWithFile(url, payload, null);
	}

	public TaobaoResponse fetchWithFile(URL url,
			Map<String, CharSequence> payload, File file)
			throws TaobaoApiException {
		TaobaoResponse rsp = null;
		PostMethod postMethod = null;
		try {
			postMethod = new PostMethod(url.toString());
			rsp = _fetch(postMethod, payload, file);
		} catch (Exception e) {
			throw new TaobaoApiException(e);
		} finally {
			 // always release the connection after we're done 
			postMethod.releaseConnection();
		}
		return rsp;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.taobao.api.UrlFetch#setKeepAlive(boolean)
	 */
	public void setKeepAlive(boolean keepAlive) {
		this.keepAlive = keepAlive;
	}

	/* (non-Javadoc)
	 * @see com.taobao.api.UrlFetch#setStaleCheckingEnabled(boolean)
	 */
	public void setStaleCheckingEnabled(boolean value) {
		this.httpClient.getHttpConnectionManager().getParams().setStaleCheckingEnabled(value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.taobao.api.UrlFetch#setDefaultMaxConnectionsPerHost(int)
	 */
	public void setMaxConnectionsPerHost(int maxHostConnections) {
		this.httpClient.getHttpConnectionManager().getParams()
				.setDefaultMaxConnectionsPerHost(maxHostConnections);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.taobao.api.UrlFetch#setMaxTotalConnections(int)
	 */
	public void setMaxTotalConnections(int maxTotalConnections) {
		this.httpClient.getHttpConnectionManager().getParams()
				.setMaxTotalConnections(maxTotalConnections);

	}

	public void setConnectTimeout(int milliSecond) {
		this.httpClient.getHttpConnectionManager().getParams()
				.setConnectionTimeout(milliSecond);
	}

	public void setReadTimeout(int milliSecond) {
		this.httpClient.getParams().setSoTimeout(milliSecond);
	}

	protected TaobaoResponse _fetch(PostMethod postMethod,
			Map<String, CharSequence> payload, File file) throws HttpException,
			IOException {
		Set<Entry<String, CharSequence>> entries = payload.entrySet();
		List<Part> paramsList = new ArrayList<Part>();
		TaobaoResponse response = new TaobaoResponse();
		for (Entry<String, CharSequence> entry : entries) {
			StringPart tempPart = new StringPart(entry.getKey(), (String) entry
					.getValue(), "utf-8");
			paramsList.add(tempPart);
		}
		RequestEntity rp = null;
		if (file != null) {
			FilePart tempFilePart = new FilePart(file.getName(), file,"image/jpeg","utf-8");
			paramsList.add(tempFilePart);
			Part[] part = new Part[paramsList.size()];
			for (int i = 0; i < paramsList.size(); i++) {
				part[i] = paramsList.get(i);
			}
			rp = new MultipartRequestEntity(part, postMethod.getParams());
			OutputStream o = new ByteArrayOutputStream();
			rp.writeRequest(o);
			response.setRequestBody(o.toString());
		} else {
			String buffer = FetchUtil.paramsToBuffer(payload.entrySet(), "&",
					"=");
			response.setRequestBody(buffer);
			rp = new StringRequestEntity(buffer,
					"application/x-www-form-urlencoded", null);
		}
		if (keepAlive) {
			postMethod.setRequestHeader("Connection", "Keep-Alive");
		}
		postMethod.setRequestEntity(rp);
		Header responseHeader = null;
		
		httpClient.executeMethod(postMethod);
		responseHeader = postMethod.getResponseHeader("sip_status");
		response.setRequestUrl(postMethod.getURI().toString());
		response.setRequestParameters(FetchUtil.paramsToBuffer(payload.entrySet(), "&",
					"="));
		String body = FetchUtil.inputStreamToString(postMethod
				.getResponseBodyAsStream());
		if (responseHeader != null) {
			String status = responseHeader.getValue();
			if (!SIP_STATUS_OK.equals(status)) {
				response.setErrorCode(status);
				response.setMsg(postMethod.getResponseHeader(
						"sip_error_message").getValue());
				if (status.equals("1004")) {
					response.setRedirectUrl(postMethod.getResponseHeader(
							"sip_isp_loginurl").getValue());
				}
			}
		}
		response.setBody(body);
		return response;
	}

}
